/**
 * @file   USB2XXX_LINEX_Test.cpp
 * @brief  LIN数据收发相关测试
 * @author wdluo(wdluo@toomoss.com)
 * @version 1.0
 * @date 2022-12-03
 * @copyright Copyright (c) 2022 重庆图莫斯电子科技有限公司
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "usb_device.h"
#include "usb2lin_ex.h"
#if _WIN32
#include <windows.h>
#pragma comment(lib,"USB2XXX.lib")
#endif

#define GET_FIRMWARE_INFO       1//获取固件信息
#define LIN_MASTER_TEST         0//主机模式读写测试
#define LIN_SLAVE_TEST          0//从机模式读写测试
#define LIN_MASTER_SCH_TEST     1//主机模式调度表测试


//检测到USB断开之后重连设备
void RetryConnect(int DevHandle, int Channel)
{
    static int retry = 0;
    int n = 0;
    do {
        if (USB_RetryConnect(DevHandle)) {
            int ret = LIN_EX_Init(DevHandle, Channel,19200,1);
            if (ret == LIN_EX_SUCCESS) {
                retry++;
                printf("重连成功次数：%d\n", retry);
                return;
            }
            return;
        }
#if _WIN32
        Sleep(100);
#else
        usleep(100*1000);
#endif
        n++;
    } while (n<100);
    printf("重连失败！！\n");
}

int main(int argc, const char* argv[])
{
#if GET_FIRMWARE_INFO
    DEVICE_INFO DevInfo;
#endif
    int DevHandle[10];
    int LINMasterIndex = 0;
    int LINSlaveIndex = 0;
    int DevIndex = 0;
    bool state;
    int ret;
    char *MSGTypeStr[]={"UN","MW","MR","SW","SR","BK","SY","ID","DT","CK"};
    char *CKTypeStr[]={"STD","EXT","USER","NONE","ERROR"};
    char t[64];
    DEV_GetDllBuildTime(t);
    printf("dll build time = %s\n",t);
    //扫描查找设备，并获取设备号
    ret = USB_ScanDevice(DevHandle);
    if (ret <= 0) {
        printf("No device connected!\n");
        return 0;
    }       
    else {
        printf("have %d device connected!\n", ret);
    }
    //打开设备
    state = USB_OpenDevice(DevHandle[DevIndex]);
    if (!state) {
        printf("Open device error!\n");
        return 0;
    }
#if GET_FIRMWARE_INFO
    char FunctionStr[256] = { 0 };
    //获取固件信息
    state = DEV_GetDeviceInfo(DevHandle[DevIndex], &DevInfo, FunctionStr);
    if (!state) {
        printf("Get device infomation error!\n");
        return 0;
    }
    else {
        printf("Firmware Info:\n");
        printf("Firmware Name:%s\n", DevInfo.FirmwareName);
        printf("Firmware Build Date:%s\n", DevInfo.BuildDate);
        printf("Firmware Version:v%d.%d.%d\n", (DevInfo.FirmwareVersion >> 24) & 0xFF, (DevInfo.FirmwareVersion >> 16) & 0xFF, DevInfo.FirmwareVersion & 0xFFFF);
        printf("Hardware Version:v%d.%d.%d\n", (DevInfo.HardwareVersion >> 24) & 0xFF, (DevInfo.HardwareVersion >> 16) & 0xFF, DevInfo.HardwareVersion & 0xFFFF);
        printf("Firmware SerialNumber:%08X%08X%08X\n", DevInfo.SerialNumber[0], DevInfo.SerialNumber[1], DevInfo.SerialNumber[2]);
    }
#endif
#if LIN_MASTER_TEST
    //初始化配置LIN
    ret = LIN_EX_Init(DevHandle[DevIndex], LINMasterIndex, 19200, LIN_EX_MASTER);
    if (ret != LIN_EX_SUCCESS) {
        printf("Config LIN failed!\n");
        return 0;
    }
    else {
        printf("Config LIN Success!\n");
    }
    LIN_EX_MSG LINMsg[5];
    LIN_EX_MSG LINOutMsg[10];
    unsigned int MsgLen = 5;
    LINMsg[0].MsgType = LIN_EX_MSG_TYPE_BK;//同步间隔，一般用于唤醒从机，也可以不要
    LINMsg[0].Timestamp = 20;//发送该帧数据之后的延时时间，最小建议设置为1
    //后面4个ID全部为主机发送数据模式
    for (int f = 1;f < MsgLen;f++) {
        LINMsg[f].MsgType = LIN_EX_MSG_TYPE_MW;//主机写数据模式
        LINMsg[f].DataLen = 8;
        for (int i = 0;i < LINMsg[1].DataLen;i++) {
            LINMsg[f].Data[i] = (f << 4) | i;
        }
        LINMsg[f].Timestamp = 50;//发送该帧数据之后的延时时间，最小建议设置为1
        LINMsg[f].CheckType = LIN_EX_CHECK_EXT;//设置好校验类型后，适配器会根据校验类型自动计算校验数据
        LINMsg[f].PID = f;
    }
    ret = LIN_EX_MasterSync(DevHandle[DevIndex], LINMasterIndex, &LINMsg[0], LINOutMsg, MsgLen);
    if (ret < LIN_EX_SUCCESS) {
        printf("MasterSync LIN failed!\n");
        RetryConnect(DevHandle[DevIndex], LINMasterIndex);
    }
    else {
        printf("MsgLen = %d\n", ret);
        for (int i = 0;i < ret;i++) {
            printf("%s SYNC[%02X] PID[%02X] ", MSGTypeStr[LINOutMsg[i].MsgType], LINOutMsg[i].Sync, LINOutMsg[i].PID);
            for (int j = 0;j < LINOutMsg[i].DataLen;j++) {
                printf("%02X ", LINOutMsg[i].Data[j]);
            }
            printf("[%s][%02X] [%02d:%02d:%02d.%03d]\n", CKTypeStr[LINOutMsg[i].CheckType], LINOutMsg[i].Check, (LINOutMsg[i].Timestamp / 3600000) % 60, (LINOutMsg[i].Timestamp / 60000) % 60, (LINOutMsg[i].Timestamp / 10000) % 60, (LINOutMsg[i].Timestamp) % 1000);
        }
    }
#elif LIN_SLAVE_TEST
    //初始化配置LIN
    ret = LIN_EX_Init(DevHandle[DevIndex], LINMasterIndex, 19200, LIN_EX_SLAVE);
    if (ret != LIN_EX_SUCCESS) {
        printf("Config LIN failed!\n");
        return 0;
    }
    else {
        printf("Config LIN Success!\n");
    }
   printf("Start Get LIN Data...\n");
    //设置ID为LIN_EX_MSG_TYPE_SW模式，这样主机就可以读取到数据
    LIN_EX_MSG LINSlaveMsg[5];
    for(int i=0;i<5;i++){
        LINSlaveMsg[i].PID = 5+i;
        LINSlaveMsg[i].CheckType = LIN_EX_CHECK_EXT;
        LINSlaveMsg[i].DataLen = 8;
        for(int j=0;j<LINSlaveMsg[i].DataLen;j++){
            LINSlaveMsg[i].Data[j]=(i<<4)|j;
        }
        LINSlaveMsg[i].MsgType = LIN_EX_MSG_TYPE_SW;//从机发送数据模式
    }
    ret = LIN_EX_SlaveSetIDMode(DevHandle[DevIndex],LINSlaveIndex,LINSlaveMsg,5);
    if(ret != LIN_EX_SUCCESS){
        printf("Config LIN ID Mode failed!\n");
        return 0;
    }else{
        printf("Config LIN ID Mode Success!\n");
    }
    //循环获取接收到的数据，该操作可以用作LIN总线数据监控
    for(int t=0;t<100;t++){
    {
        LIN_EX_MSG LINMsg[512];//缓冲区尽量大一点，防止益处
        int ret = LIN_EX_SlaveGetData(DevHandle[DevIndex],LINSlaveIndex,LINMsg);
        for(int i=0;i<ret;i++){
            printf("%s SYNC[%02X] PID[%02X] ",MSGTypeStr[LINMsg[i].MsgType],LINMsg[i].Sync,LINMsg[i].PID);
            for(int j=0;j<LINMsg[i].DataLen;j++){
                printf("%02X ",LINMsg[i].Data[j]);
            }
            printf("[%s][%02X] [%02d:%02d:%02d.%03d]\n",CKTypeStr[LINMsg[i].CheckType],LINMsg[i].Check,(LINMsg[i].Timestamp/36000000)%60,(LINMsg[i].Timestamp/600000)%60,(LINMsg[i].Timestamp/10000)%60,(LINMsg[i].Timestamp/10)%1000);
        }
#if _WIN32
        Sleep(100);
#else
        usleep(100*1000);
#endif
    }
#elif LIN_MASTER_SCH_TEST
    //初始化配置LIN
    ret = LIN_EX_Init(DevHandle[DevIndex], LINMasterIndex, 19200, LIN_EX_MASTER);
    if (ret != LIN_EX_SUCCESS) {
        printf("Config LIN failed!\n");
        return 0;
    }
    else {
        printf("Config[%d] LIN Success!\n",t);
    }
    LIN_EX_MSG LINMsg[6];
    LIN_EX_MSG LINOutMsg[10];
    unsigned int MsgLen = 6;
    LINMsg[0].MsgType = LIN_EX_MSG_TYPE_BK;
    LINMsg[0].Timestamp = 20;//当前帧发送时，下一帧数据发送的时间间隔，单位为毫秒
    LINMsg[0].Reserve1 = 1;//当前帧发送次数
    for(int f=1;f<MsgLen;f++){
        LINMsg[f].MsgType = LIN_EX_MSG_TYPE_MW;
        LINMsg[f].DataLen = 8;
        for(int i=0;i<LINMsg[1].DataLen;i++){
            LINMsg[f].Data[i] = (f<<4)|i;
        }
        LINMsg[f].Timestamp = 20;//当前帧发送时，下一帧数据发送的时间间隔，单位为毫秒
        LINMsg[f].CheckType = LIN_EX_CHECK_EXT;
        LINMsg[f].PID = f+1;
        LINMsg[f].Reserve1 = 1;//当前帧发送次数
    }
    //ID=6，该帧为主机读数据模式，也就是主机发送帧头，从机返回数据
    LINMsg[MsgLen-1].MsgType = LIN_EX_MSG_TYPE_MR;
    LINMsg[MsgLen-1].PID = 6;
    LINMsg[MsgLen-1].Timestamp = 20;
    LINMsg[MsgLen-1].Reserve1 = 1;//当前帧发送次数
    //设置调度表发送次数
    ret = LIN_EX_MasterSetSchRunTimes(DevHandle[DevIndex],LINMasterIndex,1);
    if(ret < LIN_EX_SUCCESS){
        printf("Master SetSchRunTimes failed!\n");
        return 0;
    }else{
        printf("Master SetSchRunTimes success!\n");
    }
    //启动调度表
    ret = LIN_EX_MasterStartSch(DevHandle[DevIndex],LINMasterIndex,LINMsg,MsgLen);
    if(ret < LIN_EX_SUCCESS){
        printf("Master schedule start failed!\n");
        return 0;
    }else{
        printf("Master schedule start success!\n");
    }
    //循环获取调度表数据
    for(int i=0;i<20;i++){
#if _WIN32
        Sleep(100);
#else
        usleep(100*1000);
#endif
        ret = LIN_EX_SlaveGetData(DevHandle[DevIndex],LINMasterIndex,LINOutMsg);
        if(ret < LIN_EX_SUCCESS){
            printf("Master get schedule failed!\n");
            return 0;
        }else{
            printf("[0]MsgLen = %d\r\n",ret);
            for(int i=0;i<ret;i++){
                printf("[0]%s SYNC[%02X] PID[%02X] ",MSGTypeStr[LINOutMsg[i].MsgType],LINOutMsg[i].Sync,LINOutMsg[i].PID);
                for(int j=0;j<LINOutMsg[i].DataLen;j++){
                    printf("%02X ",LINOutMsg[i].Data[j]);
                }
                printf("CK = %d ",LINOutMsg[i].CheckType);
                printf("Timestamp = %d",LINOutMsg[i].Timestamp);
                printf("\r\n");
            }
        }
    }
    //停止调度表
    LIN_EX_MasterStopSch(DevHandle[DevIndex],LINMasterIndex);
#endif
    USB_CloseDevice(DevHandle[DevIndex]);
	return 0;
}

